/*
 * This file or a portion of this file is licensed under the terms of
 * the Globus Toolkit Public License, found in file ../GTPL, or at
 * http://www.globus.org/toolkit/download/license.html. This notice must
 * appear in redistributions of this file, with or without modification.
 *
 * Redistributions of this Software, with or without modification, must
 * reproduce the GTPL in: (1) the Software, or (2) the Documentation or
 * some other similar material which is provided with the Software (if
 * any).
 *
 * Copyright 1999-2004 University of Chicago and The University of
 * Southern California. All rights reserved.
 */

package org.griphyn.vdl.classes;

import org.griphyn.vdl.classes.*;
import java.util.*;
import java.io.IOException;
import java.io.Writer;
import java.io.Serializable;

/**
 * <code>Transformation</code> is an implementation of an abstract VDL
 * <code>Definition</code>. A transformation describes the immutable
 * template of an input, processing, and output (IPO) application. The
 * environment is part of the capture. The template can be parametrized
 * using formal arguments, which are part of the transformation
 * definition. Think of a transformation as something similar to a C
 * function definition. Mutable parts are hidden in the arguments, and
 * instantiated in <code>Derivation</code>.<p>
 *
 * FIXME: The mixing of compounds with simple transformations within
 * the same class instead of hierarchy needs some serious redesigning.
 * Unfortunately, this entails a redesign of just about everything...
 *
 * @author Jens-S. Vöckler
 * @author Yong Zhao
 * @version $Revision: 50 $
 *
 * @see Definition
 * @see Definitions
 * @see Derivation 
 */
public class Transformation extends Definition // thus implements VDL
  implements Serializable
{
  /**
   * Captures the argument name indexed map of formal arguments. Each 
   * element is of type {@link Declare} with an optional default value
   * of type {@link Value}.
   *
   * @see Declare
   */
  private TreeMap m_declareMap;   

  /**
   * The profile list encapsulates scheduler specific data in a 
   * generic structure. 
   *
   * @see Profile
   */
  private ArrayList m_profileList;

  /**
   * The argument list describes the command line arguments as sum of
   * substrings. Each element is an {@link Argument}, and can be
   * optionally addressed by a name. 
   *
   * @see Argument 
   */
  private ArrayList m_argumentList;

  /**
   * All arguments are, for ease-of-use, separated by a single
   * space. If this is not wanted, the user can overwrite the
   * separator, including setting it to the empty string and null.
   */
  private String m_argumentSeparator = " ";

  /**
   * Each compound transformation may declare local variables.
   */
  private TreeMap m_localMap;

  /**
   * Each entry in the call list describes an invocation of another
   * transformation, complete with actual arguments etc. 
   *
   * @see Call 
   */
  private ArrayList m_callList;

  /**
   * Type accessor for quick distinction between definitions.
   * @return the value of {@link Definition#TRANSFORMATION}
   */
  public int getType()
  {
    return Definition.TRANSFORMATION;
  }

  /**
   * Default ctor: This ctor will frequently be used by the SAX parser
   * to create a hollow instance. Note that a transformation can be named. 
   */
  public Transformation() 
  {
    super();
    this.m_argumentList = new ArrayList();
    this.m_declareMap = new TreeMap();
    this.m_profileList = new ArrayList();
    this.m_localMap = new TreeMap();
    this.m_callList = new ArrayList();
  }

  /** 
   * Minimum c'tor: Any transformation should be named.
   *
   * @param name is the name of the transformation in the current namespace
   *        and version context.
   * @see Definition
   */
  public Transformation( String name ) 
  {
    super(name);
    this.m_argumentList = new ArrayList();
    this.m_declareMap = new TreeMap();
    this.m_profileList = new ArrayList();
    this.m_localMap = new TreeMap();
    this.m_callList = new ArrayList();
  }

  /**
   * Standard ctor: This will name a transformation with the complete
   * tripel necessary to access it correctly (w/o guessing).
   *
   * @param name is the name of the transformation in the current namespace
   *        and version context.
   * @param namespace is the namespace that the transformation resides in.
   * @param version is a version number. 
   *
   * @see Definition
   */
  public Transformation( String namespace, String name, String version ) 
  {
    super(namespace,name,version);
    this.m_argumentList = new ArrayList();
    this.m_declareMap = new TreeMap();
    this.m_profileList = new ArrayList();
    this.m_localMap = new TreeMap();
    this.m_callList = new ArrayList();
  }

  /**
   * Accessor: Adds an argument to the list of arguments
   *
   * @param vArgument is the argument to append to the command line
   *        arguments.
   * @exception IndexOutOfBounds if the argument does not fit into the list.
   * @see Argument
   */
  public void addArgument( Argument vArgument )
    throws IndexOutOfBoundsException
  { 
    this.m_argumentList.add(vArgument); 
  }

  /** 
   * Accessor: Inserts an argument at an arbitrary place into the list.
   * Each component in this vector with an index greater or equal to the
   * specified index is shifted upward to have an index one greater than
   * the value it had previously.
   *
   * @param index is the position to insert an argument
   * @param vArgument is the argument to append to the command line
   *        arguments.
   * @exception IndexOutOfBounds if the argument does not fit into the list.
   * @see #getArgument( int )
   * @see #setArgument( int, Argument )
   * @see Argument
   */
  public void addArgument( int index, Argument vArgument )
    throws IndexOutOfBoundsException
  { 
    this.m_argumentList.add(index, vArgument); 
  }

  /**
   * Accessor: Adds an invocation to the list of calls.
   *
   * @param vCall is the invocation to append to the list of calls.
   * @exception IndexOutOfBounds if the argument does not fit into the list.
   * @see Call
   */
  public void addCall( Call vCall )
    throws IndexOutOfBoundsException
  { 
    this.m_callList.add(vCall); 
  }

  /** 
   * Accessor: Inserts an invocation at an arbitrary place into the list.
   * Each component in this vector with an index greater or equal to the
   * specified index is shifted upward to have an index one greater than
   * the value it had previously.
   *
   * @param index is the position to insert an invocation
   * @param vCall is the invocation to append to the list of calls.
   * @exception IndexOutOfBounds if the argument does not fit into the list.
   * @see #setCall( int, Call )
   * @see #getCall( int )
   * @see Call
   */
  public void addCall( int index, Call vCall )
    throws IndexOutOfBoundsException
  { 
    this.m_callList.add(index, vCall); 
  }

  /**
   * Accessor: Adds a formal argument declaration to the map of declarations.
   * 
   * @param vDeclare is the formal argument to add to the declarations.
   * @see #getDeclare( String )
   * @see #setDeclare( Declare )
   * @see Declare
   */
  public void addDeclare( Declare vDeclare )
  {
    this.m_declareMap.put( vDeclare.getName(), vDeclare ); 
  }

  /**
   * Accessor: Adds a temporary variable declaration to the map of local
   * variables.
   * 
   * @param vLocal is the local variable declaration with value.
   * @see #getLocal( String )
   * @see #setLocal( Local )
   * @see Local
   */
  public void addLocal( Local vLocal )
  {
    this.m_localMap.put( vLocal.getName(), vLocal ); 
  }

  /**
   * Accessor: Appends a profile definition to the list of profiles.
   * 
   * @param vProfile is the profile to append to remembered profiles.
   * @exception IndexOutOfBounds if the argument does not fit into the list.
   * @see Profile
   */
  public void addProfile( Profile vProfile )
    throws IndexOutOfBoundsException
  { 
    this.m_profileList.add(vProfile); 
  }


  /**
   * Accessor: Inserts a profile definition at an arbitrary position
   * into the list of profiles. Each component in this vector with an
   * index greater or equal to the specified index is shifted upward to
   * have an index one greater than the value it had previously.
   * 
   * @param index is the position to insert the definitions into.
   * @param vProfile is the profile to append to remembered profiles.
   * @exception IndexOutOfBounds if the argument does not fit into the list.
   * @see #getProfile( int )
   * @see #setProfile( int, Profile )
   * @see Profile
   */
  public void addProfile( int index, Profile vProfile )
    throws IndexOutOfBoundsException
  {
    this.m_profileList.add(index, vProfile); 
  }

  /**
   * Accessor: Provides an iterator for the <code>Argument</code> list.
   *
   * @return the iterator for the <code>Argument</code> list.
   * @see Argument
   * @see java.util.Enumeration
   * @deprecated Use the new Collection based interfaces
   */
  public Enumeration enumerateArgument()
  {
    return Collections.enumeration(this.m_argumentList);
  }

  /**
   * Accessor: Provides an iterator for the <code>Call</code> list.
   *
   * @return the iterator for the <code>Call</code> list.
   * @see Call
   * @see java.util.Enumeration
   * @deprecated Use the new Collection based interfaces
   */
  public Enumeration enumerateCall()
  {
    return Collections.enumeration(this.m_callList);
  }

  /**
   * Accessor: Provides an iterator for the <code>Declare</code> map.
   *
   * @return the iterator for the <code>Declare</code> list.
   * @see Declare
   * @see java.util.Enumeration
   * @deprecated Use the new Collection based interfaces
   */
  public Enumeration enumerateDeclare()
  {
    return Collections.enumeration(this.m_declareMap.values()); 
  }

  /**
   * Accessor: Provides an iterator for the local variables.
   *
   * @return the iterator over all locally declared variables. 
   * @see Local
   * @see java.util.Enumeration
   * @deprecated Use the new Collection based interfaces
   */
  public Enumeration enumerateLocal()
  {
    return Collections.enumeration(this.m_localMap.values()); 
  }

  /**
   * Accessor: Provides an iterator for the <code>Profile</code> list.
   *
   * @return the iterator for the <code>Profile</code> list.
   * @see Profile
   * @see java.util.Enumeration
   * @deprecated Use the new Collection based interfaces
   */
  public Enumeration enumerateProfile()
  {
    return Collections.enumeration(this.m_profileList); 
  }

  /**
   * Predicate: Determines, if this is a simple TR (as opposed to compound).
   * @return true, if this a simple transformation.
   */
  public boolean isSimple() 
  {
    return this.m_callList.size() == 0; 
  }

  /**
   * Accessor: Obtains an <code>Argument</code> at an arbitrary position.
   *
   * @param index is the place to look up the element at.
   * @return the argument at the specified place.
   * @throws IndexOutOfBoundsException if the referenced position does
   * not exist.
   * @see Argument
   */
  public Argument getArgument(int index)
    throws IndexOutOfBoundsException
  {
    //-- check bounds for index
    if ((index < 0) || (index >= this.m_argumentList.size())) {
      throw new IndexOutOfBoundsException();
    }
    
    return (Argument) this.m_argumentList.get(index);
  }

  /**
   * Accessor: Obtains the complete commandline arguments. This array is
   * a copy to avoid write-through modifications.
   * 
   * @return an array with all commandline arguments inside.
   * @see #setArgument( Argument[] )
   * @see Argument
   * @deprecated Use the new Collection based interfaces
   */
  public Argument[] getArgument()
  {
    int size = this.m_argumentList.size();
    Argument[] mArray = new Argument[size];
    System.arraycopy( this.m_argumentList.toArray(new Argument[0]), 0,
		      mArray, 0, size );
    return mArray;
  }

  /**
   * Accessor: Obtains the count of items in the argument list.
   *
   * @return the number of arguments in the commandline argument list.
   * @see Argument
   */
  public int getArgumentCount()
  { 
    return this.m_argumentList.size(); 
  }

  /**
   * Accessor: Obtains the complete commandline arguments. The resulting
   * list is read-only.
   * 
   * @return an array with all commandline arguments inside.
   * @see #setArgument( Collection )
   * @see Argument 
   */
  public java.util.List getArgumentList()
  {
    return Collections.unmodifiableList(this.m_argumentList);
  }

  /**
   * Gets the separating string between multiple {@link Argument}
   * elements.
   *
   * @return The current state of the separator. The text may be null.
   * @see #setArgumentSeparator(String)
   */
  public String getArgumentSeparator()
  { 
    return this.m_argumentSeparator;
  }

  /**
   * Accessor: Obtains an <code>Call</code> at an arbitrary position.
   *
   * @param index is the place to look up the element at.
   * @return the call at the specified place.
   * @throws IndexOutOfBoundsException if the referenced position does
   * not exist.
   * @see #addCall( int, Call )
   * @see #setCall( int, Call )
   * @see Call
   */
  public Call getCall(int index)
    throws IndexOutOfBoundsException
  {
    //-- check bounds for index
    if ((index < 0) || (index >= this.m_callList.size())) {
      throw new IndexOutOfBoundsException();
    }
    
    return (Call) this.m_callList.get(index);
  } //-- Call getCall(int) 

  /**
   * Accessor: Obtains the invocation list. This
   * array is a copy of the original to avoid write-through modifications.
   *
   * @return an array with all calls inside.
   * @see Call
   * @deprecated Use the new Collection based interfaces
   */
  public Call[] getCall()
  {
    int size = this.m_callList.size();
    Call[] mArray = new Call[size];
    System.arraycopy( this.m_callList.toArray( new Call[0] ), 0,
		      mArray, 0, size );
    return mArray;
  }

  /**
   * Accessor: Obtains the count of items in the call list.
   *
   * @return the number of calls in the call list.
   * @see Call
   */
  public int getCallCount()
  { 
    return this.m_callList.size(); 
  }

  /**
   * Accessor: Obtains the invocation list.
   * @return a read-only list with all calls inside.
   * @see #setCall( Collection )
   * @see Call
   */
  public java.util.List getCallList()
  {
    return Collections.unmodifiableList( this.m_callList );
  }

  /**
   * Accessor: Obtains the declaration of a formal argument as referenced
   * by its variable name. 
   *
   * @param name is the symbolic index and variable name to obtain the
   *        declaration for.
   * @return the <code>Declare</code> object referenced by the name. May
   *         return <code>null</code>, if there is no such object.
   * @see #addDeclare( Declare )
   * @see #setDeclare( Declare )
   * @see Declare
   */
  public Declare getDeclare(String name)
  { 
    return (Declare) this.m_declareMap.get(name); 
  }

  /**
   * Accessor: Obtain all known formal arguments. Note that the array
   * will be arbitrarily sorted, depending on Java's hash function.
   *
   * @return a list of all formal arguments.
   * @see Declare
   * @see #setDeclare( Declare[] )
   * @deprecated Use the new Collection based interfaces
   */
  public Declare[] getDeclare()
  {
    int size = this.m_declareMap.size();
    Declare[] mArray = new Declare[size];
    this.m_declareMap.values().toArray(mArray);
    return mArray;
  }

  /**
   * Accessor: Counts the number of formal arguments known to this 
   * transformation. 
   *
   * @return the formal argument count
   */
  public int getDeclareCount()
  { 
    return this.m_declareMap.size(); 
  }

  /**
   * Accessor: Obtain all known formal arguments. Note that the list
   * will be arbitrarily sorted, depending on Java's hash function.
   * It is also a read-only list to avoid modifications outside the API.
   *
   * @return a list of all formal arguments.
   * @see #setDeclare( Collection )
   * @see Declare
   */
  public java.util.List getDeclareList()
  {
    return Collections.unmodifiableList( new ArrayList(this.m_declareMap.values()) );
  }

  /**
   * Accessor: Obtains all known formal arguments. The map is a read-only
   * map to avoid modifications outside the API.
   *
   * @return a map with all formal arguments.
   * @see #setDeclare( Map )
   * @see Declare 
   */
  public java.util.Map getDeclareMap()
  {
    return Collections.unmodifiableMap( this.m_declareMap );
  }

  /**
   * Accessor: Obtains the declaration of a temporary variable as
   * referenced by its name.
   *
   * @param name is the variable name to obtain the declaration for.
   * @return the <code>Local</code> object referenced by the name. May
   *         return <code>null</code>, if there is no such object.
   * @see #addLocal( Local )
   * @see #setLocal( Local )
   * @see Local
   */
  public Local getLocal(String name)
  { 
    return (Local) this.m_localMap.get(name); 
  }

  /**
   * Accessor: Obtain all known temporary variables. Note that the array
   * will be arbitrarily sorted, depending on Java's hash function.
   *
   * @return a list of all temporary variables. 
   * @see #setLocal( Local[] )
   * @see Local
   * @deprecated Use the new Collection based interfaces
   */
  public Local[] getLocal()
  {
    int size = this.m_localMap.size();
    Local[] mArray = new Local[size];
    this.m_localMap.values().toArray(mArray);
    return mArray;
  }

  /**
   * Accessor: Counts the number of temporary variables known to this 
   * transformation. 
   *
   * @return the temporary variable count
   */
  public int getLocalCount()
  { 
    return this.m_localMap.size(); 
  }

  /**
   * Accessor: Obtains all known temporary variables. Note that the list
   * will be arbitrarily sorted, depending on Java's hash function. It
   * is also a read-only list to avoid modifications outside the API.
   *
   * @return a list of all formal arguments.
   * @see #setLocal( Collection )
   * @see Local
   */
  public java.util.List getLocalList()
  {
    return 
      Collections.unmodifiableList( new ArrayList(this.m_localMap.values()) );
  }

  /**
   * Accessor: Obtains all known temporary variables. The map is a
   * read-only map to avoid modifications outside the API.
   *
   * @return a map with all formal arguments.
   * @see #setLocal( Map )
   * @see Local 
   */
  public java.util.Map getLocalMap()
  {
    return Collections.unmodifiableMap( this.m_localMap );
  }

  /**
   * Accessor: Obtains an <code>Profile</code> at an arbitrary position.
   *
   * @param index is the place to look up the element at.
   * @exception IndexOutOfBoundsException if the referenced position
   * does not exist. 
   * @see #addProfile( int, Profile )
   * @see #setProfile( int, Profile )
   * @see Profile
   */
  public Profile getProfile(int index)
    throws IndexOutOfBoundsException
  {
    //-- check bounds for index
    if ((index < 0) || (index >= this.m_profileList.size())) {
      throw new IndexOutOfBoundsException();
    }
    
    return (Profile) this.m_profileList.get(index);
  }
  
  /**
   * Accessor: Obtain a copy of the list of all <code>Profile</code>
   * specifications.
   *
   * @return a collection containing the scheduler specific environment
   *         options for the job.
   * @see #setProfile( Profile[] )
   * @see Profile 
   * @deprecated Use the new Collection based interfaces
   */
  public Profile[] getProfile()
  {
    int size = this.m_profileList.size();
    Profile[] mProfile = new Profile[size];
    System.arraycopy( this.m_profileList.toArray(new Profile[0]), 0,
		      mProfile, 0, size );
    return mProfile;
  }

  /**
   * Accessor: Counts the number of profile specifications known to this job.
   * 
   * @return the number of profiles
   * @see Profile
   */
  public int getProfileCount()
  {
    return this.m_profileList.size(); 
  }
  
  /**
   * Accessor: Obtain a read-only copy of the list of all
   * <code>Profile</code> specifications.
   *
   * @return a collection containing the scheduler specific environment
   *         options for the job.
   * @see #setProfile( Collection )
   * @see Profile 
   */
  public java.util.List getProfileList()
  {
    return Collections.unmodifiableList(this.m_profileList);
  }

  /**
   * Accessor: Provides an iterator for the <code>Argument</code> list.
   *
   * @return the iterator for the <code>Argument</code> list.
   * @see Argument
   */
  public Iterator iterateArgument()
  {
    return this.m_argumentList.iterator();
  }

  /**
   * Accessor: Provides an iterator for the <code>Call</code> list.
   *
   * @return the iterator for the <code>Call</code> list.
   * @see Call
   */
  public Iterator iterateCall()
  {
    return this.m_callList.iterator();
  }

  /**
   * Accessor: Provides an iterator for the <code>Declare</code> map.
   *
   * @return the iterator for the <code>Declare</code> list.
   * @see Declare
   */
  public Iterator iterateDeclare()
  {
    return this.m_declareMap.values().iterator();
  }

  /**
   * Accessor: Provides an iterator for local variables. 
   *
   * @return the iterator over all local variables. 
   * @see Declare
   */
  public Iterator iterateLocal()
  {
    return this.m_localMap.values().iterator();
  }

  /**
   * Accessor: Provides an iterator for the <code>Profile</code> list.
   *
   * @return the iterator for the <code>Profile</code> list.
   * @see Profile
   */
  public Iterator iterateProfile()
  {
    return this.m_profileList.iterator(); 
  }
  /**
   * Accessor: Provides a list iterator for the <code>Argument</code> list.
   *
   * @return the iterator for the <code>Argument</code> list.
   * @see Argument
   */
  public ListIterator listIterateArgument()
  {
    return this.m_argumentList.listIterator();
  }

  /**
   * Accessor: Provides a list iterator for the <code>Call</code> list.
   *
   * @return the iterator for the <code>Call</code> list.
   * @see Call
   */
  public ListIterator listIterateCall()
  {
    return this.m_callList.listIterator();
  }

  /**
   * Accessor: Provides a list iterator for the <code>Profile</code> list.
   *
   * @return the iterator for the <code>Profile</code> list.
   * @see Profile
   */
  public ListIterator listIterateProfile()
  {
    return this.m_profileList.listIterator(); 
  }

  /**
   * Accessor: Provides a list iterator for the <code>Argument</code> list.
   *
   * @return the iterator for the <code>Argument</code> list.
   * @param start is the start index
   * @see Argument
   */
  public ListIterator listIterateArgument( int start )
  {
    return this.m_argumentList.listIterator(start);
  }

  /**
   * Accessor: Provides a list iterator for the <code>Call</code> list.
   *
   * @return the iterator for the <code>Call</code> list.
   * @param start is the start index
   * @see Call
   */
  public ListIterator listIterateCall( int start )
  {
    return this.m_callList.listIterator(start);
  }

  /**
   * Accessor: Provides a list iterator for the <code>Profile</code> list.
   *
   * @return the iterator for the <code>Profile</code> list.
   * @param start is the start index
   * @see Profile
   */
  public ListIterator listIterateProfile( int start )
  {
    return this.m_profileList.listIterator(start); 
  }

  /**
   * Accessor: Removes all commandline arguments.
   * @see Argument
   */
  public void removeAllArgument()
  {
    this.m_argumentList.clear(); 
  }

  /**
   * Accessor: Removes all calls.
   * @see Call
   */
  public void removeAllCall()
  {
    this.m_callList.clear(); 
  }

  /**
   * Accessor: Removes all formal arguments.
   * @see Declare
   */
  public void removeAllDeclare()
  { 
    this.m_declareMap.clear(); 
  }

  /**
   * Accessor: Removes all temporary variables.
   * @see Local
   */
  public void removeAllLocal()
  { 
    this.m_localMap.clear(); 
  }

  /**
   * Accessor: Removes all profile declarations.
   * @see Profile
   */
  public void removeAllProfile()
  {
    this.m_profileList.clear(); 
  }

  /**
   * Accessor: Removes a commandline argument fragment from the
   * commandline. Each component in this vector with an index greater or
   * equal to the specified index is shifted downward to have an index
   * one smaller than the value it had previously. The size of this
   * vector is decreased by 1.
   *
   * @param index is the position to remove the argument fragment from.
   * @return the removed Argument.
   * @exception ArrayIndexOutOfBoundsException if the index was invalid.
   * @see Argument
   */
  public Argument removeArgument(int index)
  {
    return (Argument) this.m_argumentList.remove(index);
  }

  /**
   * Accessor: Removes a single call fragment from the list of calls.
   * Each component in this vector with an index greater or equal to the
   * specified index is shifted downward to have an index one smaller
   * than the value it had previously. The size of this vector is
   * decreased by 1.
   *
   * @param index is the position to remove the call fragment from.
   * @return the removed Call.
   * @exception ArrayIndexOutOfBoundsException if the index was invalid.
   * @see Call
   */
  public Call removeCall(int index)
  {
    return (Call) this.m_callList.remove(index);
  }

  /**
   * Accessor: Removes a formal argument. 
   *
   * @param name is the name of the argument to remove 
   * @return the removed formal argument.
   * @see Declare
   */
  public Declare removeDeclare(String name)
  {
    return (Declare) this.m_declareMap.remove(name);
  }

  /**
   * Accessor: Removes a temporary variable.
   *
   * @param name is the name of the temporary variable to remove 
   * @return the removed variable.
   * @see Local
   */
  public Local removeLocal(String name)
  {
    return (Local) this.m_localMap.remove(name);
  }

  /**
   * Accessor: Removes a profile. Each component in this vector with an
   * index greater or equal to the specified index is shifted downward
   * to have an index one smaller than the value it had previously. The
   * size of this vector is decreased by 1.
   *
   * @param index is the position to remove the profile from.
   * @return the removed Profile.
   * @exception ArrayIndexOutOfBoundsException if the index was invalid.
   * @see Profile
   */
  public Profile removeProfile(int index)
  {
    return (Profile) this.m_profileList.remove(index);
  }

  /**
   * Accessor: Overwrites an commandline argument fragment with a new one.
   *
   * @param index is the position to overwrite the element at
   * @param vArgument is the new commandline argument.
   * @exception IndexOutOfBoundsException if the position does not exist.
   * @see Argument
   */
  public void setArgument(int index, Argument vArgument)
    throws IndexOutOfBoundsException
  {
    //-- check bounds for index
    if ((index < 0) || (index >= this.m_argumentList.size())) {
      throw new IndexOutOfBoundsException();
    }
    this.m_argumentList.set(index, vArgument);
  }

  /**
   * Accessor: Replace the commandline arguments with a new commandline
   * argument. 
   *
   * @param argumentArray is the new commandline argument array.
   * @see Argument
   * @deprecated Use the new Collection based interfaces
   */
  public void setArgument(Argument[] argumentArray)
  {
    this.m_argumentList.clear();
    this.m_argumentList.addAll( Arrays.asList(argumentArray) );
  }

  /**
   * Accessor: Replace the commandline arguments with a new commandline
   * argument. 
   *
   * @param arguments is the new commandline argument array.
   * @see Argument
   */
  public void setArgument(java.util.Collection arguments)
  {
    this.m_argumentList.clear();
    this.m_argumentList.addAll(arguments);
  }

  /**
   * Overwrites the internal separator string between neighbouring
   * {@link Argument} elements with new content. 
   *
   * @param separator is the new string separating neighbouring arguments.
   * @see #getArgumentSeparator()
   **/
  public void setArgumentSeparator( String separator )
  { 
    this.m_argumentSeparator = separator; 
  }

  /**
   * Accessor: Overwrites a call with a new one.
   *
   * @param index is the position to overwrite the element at
   * @param vCall is the new call.
   * @exception IndexOutOfBoundsException if the position does not exist.
   * @see #addCall( int, Call )
   * @see #getCall( int )
   * @see Call
   */
  public void setCall(int index, Call vCall)
    throws IndexOutOfBoundsException
  {
    //-- check bounds for index
    if ((index < 0) || (index >= this.m_callList.size())) {
      throw new IndexOutOfBoundsException();
    }
    this.m_callList.set(index, vCall);
  }

  /**
   * Accessor: Replace the calls with a new call list. 
   *
   * @param callArray is the new call array.
   * @see Call
   * @deprecated Use the new Collection based interfaces
   */
  public void setCall(Call[] callArray)
  {
    this.m_callList.clear();
    this.m_callList.addAll( Arrays.asList(callArray) );
  }

  /**
   * Accessor: Replace the calls with a new call list. 
   *
   * @param calls is the new call array.
   * @see Call
   */
  public void setCall( Collection calls )
  {
    this.m_callList.clear();
    this.m_callList.addAll(calls);
  }

  /**
   * Accessor: Insert or replace a declaration with a new version.
   *
   * @param vDeclare is the declaration to insert or replace. 
   * @see Declare
   * @see java.util.Hashtable#put( Object, Object )
   */
  public void setDeclare(Declare vDeclare)
  { 
    this.m_declareMap.put(vDeclare.getName(),vDeclare); 
  }

  /**
   * Accessor: Replace all declarations by a new set of declarations.
   * This method effectively exchanges all formal arguments of a job.
   *
   * @param declareArray is the new set of declarations.
   * @see Declare
   * @deprecated Use the new Collection based interfaces
   */
  public void setDeclare(Declare[] declareArray)
  {
    //-- copy array
    this.m_declareMap.clear();
    for (int i = 0; i < declareArray.length; i++) {
      this.m_declareMap.put(declareArray[i].getName(),declareArray[i]);
    }
  }

  /**
   * Accessor: Replace all declarations by a new set of declarations.
   * This method effectively exchanges all formal arguments of a job.
   *
   * @param declares is the new set of declarations.
   * @see Declare
   */
  public void setDeclare( Collection declares )
  {
    this.m_declareMap.clear();
    for ( Iterator i=declares.iterator(); i.hasNext(); ) {
      Declare d = (Declare) i.next();
      this.m_declareMap.put(d.getName(), d);
    }
  }
  /**
   * Accessor: Replace all declarations by a new set of declarations.
   * This method effectively exchanges all formal arguments of a job.
   *
   * @param declares is the new set of declarations.
   * @see Declare
   */
  public void setDeclare( Map declares )
  {
    this.m_declareMap.clear();
    this.m_declareMap.putAll(declares);
  }

  /**
   * Accessor: Inserts or replaces a temporary variable with a new
   * version.
   *
   * @param vLocal is the temporary variable to insert or replace. 
   * @see Local
   * @see java.util.Hashtable#put( Object, Object )
   */
  public void setLocal(Local vLocal)
  { 
    this.m_localMap.put(vLocal.getName(),vLocal); 
  }

  /**
   * Accessor: Replaces all declarations by a new set of declarations.
   * This method effectively exchanges all temporary variables.
   *
   * @param localArray is the new set of local variable declarations.
   * @see Local
   * @deprecated Use the new Collection based interfaces
   */
  public void setLocal(Local[] localArray)
  {
    //-- copy array
    this.m_localMap.clear();
    for (int i = 0; i < localArray.length; i++) {
      this.m_localMap.put(localArray[i].getName(),localArray[i]);
    }
  }

  /**
   * Accessor: Replaces all declarations by a new set of declarations.
   * This method effectively exchanges all temporary variables. 
   *
   * @param locals is the new set of temporary variable declarations.
   * @see Local
   */
  public void setLocal( Collection locals )
  {
    this.m_localMap.clear();
    for ( Iterator i=locals.iterator(); i.hasNext(); ) {
      Local d = (Local) i.next();
      this.m_localMap.put(d.getName(), d);
    }
  }

  /**
   * Accessor: Replace all declarations by a new set of declarations.
   * This method effectively exchanges all temporary variables. 
   *
   * @param locals is the new set of declarations.
   * @see Local
   */
  public void setLocal( Map locals )
  {
    this.m_localMap.clear();
    this.m_localMap.putAll(locals);
  }

  /**
   * Accessor: Overwrites a profile with a new profile
   *
   * @param index is the position to overwrite the profile at.
   * @param vProfile is the new profile to use in overwriting.
   * @exception IndexOutOfBoundsException if the position does not exist.
   * @see Profile
   */
  public void setProfile(int index, Profile vProfile)
    throws IndexOutOfBoundsException
  {
    //-- check bounds for index
    if ((index < 0) || (index >= this.m_profileList.size())) {
      throw new IndexOutOfBoundsException();
    }
    this.m_profileList.set(index, vProfile);
  }

  /**
   * Accessor: Replace the internal profiles with a new list.
   *
   * @param profileArray is the new list of profiles to use for the job.
   * @see Profile
   * @deprecated Use the new Collection based interfaces
   */
  public void setProfile( Profile[] profileArray )
  {
    this.m_profileList.clear();
    this.m_profileList.addAll( Arrays.asList(profileArray) );
  }

  /**
   * Accessor: Replace the internal profiles with a new list.
   *
   * @param profiles is the new list of profiles to use for the job.
   * @see Profile
   */
  public void setProfile( java.util.Collection profiles )
  {
    this.m_profileList.clear();
    this.m_profileList.addAll( profiles );
  }

  /** 
   * Constructs dynamically a short descriptive, hopefully unique
   * identifier for this transformation.
   *
   * @return a string describing the transformation
   * @see Object#hashCode()
   */
  public String identify()
  {
    // short and long ID are identical for transformations.
    return super.shortID();
  }

  /**
   * Dumps the content of the given element into a string. This function 
   * traverses all sibling classes as necessary and converts the 
   * data into textual output. 
   */
  public void toString( Writer stream )
    throws IOException
  {
    String newline = System.getProperty( "line.separator", "\r\n" );

    stream.write( "TR " );
    stream.write( this.identify() );
    stream.write( '(' );

    // write formal args
    if ( this.m_declareMap.size() > 0 ) {
      stream.write( newline );
      for ( Iterator i=this.m_declareMap.values().iterator(); i.hasNext(); ) {
	stream.write('\t');
	((Declare) i.next()).toString(stream);
	if ( i.hasNext() ) stream.write("," + newline);
      }
    }

    stream.write( " )" );
    stream.write( newline );
    stream.write( '{' );
    stream.write( newline );

    if ( this.isSimple() ) {
      for ( Iterator i=this.m_argumentList.iterator(); i.hasNext(); ) {
	stream.write( "  " );
	((Argument) i.next()).toString(stream);
	stream.write( ';' );
	stream.write(newline);
      }
    } else {
      if ( this.m_localMap.size() > 0 ) {
	for ( Iterator i=this.m_localMap.values().iterator(); i.hasNext(); ) {
	  stream.write( "  " );
	  ((Local) i.next()).toString(stream);
	  stream.write( ';' );
	  stream.write(newline);
	}
	stream.write(newline);
      }

      for ( Iterator i=this.m_callList.iterator(); i.hasNext(); ) {
	stream.write( "  " );
	((Call) i.next()).toString(stream);
	stream.write( ';' );
	stream.write(newline);
      }
    }

    for ( Iterator i=this.m_profileList.iterator(); i.hasNext(); ) {
      stream.write( "  " );
      ((Profile) i.next()).toString(stream);
      stream.write( ';' );
      stream.write(newline);
    }

    stream.write( '}' );
    stream.write(newline);
  }

  /**
   * Dump the state of the current element as XML output. This function
   * traverses all sibling classes as necessary, and converts the data
   * into pretty-printed XML output. The stream interface should be able
   * to handle large output efficiently, if you use a buffered writer.
   *
   * @param stream is a stream opened and ready for writing. This can also
   * be a string stream for efficient output.
   * @param indent is a <code>String</code> of spaces used for pretty
   * printing. The initial amount of spaces should be an empty string.
   * The parameter is used internally for the recursive traversal.
   * If a <code>null</code> value is specified, no indentation nor
   * linefeeds will be generated. 
   * @param namespace is the XML schema namespace prefix. If neither
   * empty nor null, each element will be prefixed with this prefix,
   * and the root element will map the XML namespace. 
   * @exception IOException if something fishy happens to the stream.
   */
  public void toXML( Writer stream, String indent, String namespace )
    throws IOException
  {
    String newline = System.getProperty( "line.separator", "\r\n" );
    String tag = ( namespace != null && namespace.length() > 0 ) ?
      namespace + ":transformation" : "transformation";

    if ( indent != null && indent.length() > 0 ) stream.write( indent );
    stream.write( '<' );
    stream.write( tag );
    super.toXML(stream);

    // add argument separator, if it is not a single space.
    // FIXME: This attribute can only appear in simple TR!
    if ( this.m_argumentSeparator == null || 
	 ! this.m_argumentSeparator.equals(" ") ) {
      stream.write( " argumentSeparator=\"");
      if ( this.m_argumentSeparator != null )
	stream.write( quote(this.m_argumentSeparator,true) );
      stream.write('"');
    }

    if ( this.m_declareMap.size() + this.m_argumentList.size() + 
	 this.m_callList.size() + this.m_profileList.size() == 0 ) {
      // empty transformation, no fargs, no body
      // FIXME: A compound TR must not be empty!
      stream.write( "/>" );
    } else {
      // done with opening tag
      stream.write( '>' );
      if ( indent != null ) stream.write( newline );

      String newindent = indent==null ? null : indent + "  ";
      for ( Iterator i=this.m_declareMap.values().iterator(); i.hasNext(); ) {
	((Declare) i.next()).toXML( stream, newindent, namespace );
      }

      if ( this.isSimple() ) {
	for ( Iterator i=this.m_argumentList.iterator(); i.hasNext(); ) {
	  ((Argument) i.next()).toXML( stream, newindent, namespace );
	}
      } else {
	for ( Iterator i=this.m_localMap.values().iterator(); i.hasNext(); ) {
	  ((Local) i.next()).toXML( stream, newindent, namespace );
	}

	// a compound TR must have at least one call!
	for ( Iterator i=this.m_callList.iterator(); i.hasNext(); ) {
	  ((Call) i.next()).toXML( stream, newindent, namespace );
	}
      }

      for ( Iterator i=this.m_profileList.iterator(); i.hasNext(); ) {
	((Profile) i.next()).toXML( stream, newindent, namespace );
      }

      // close tag
      if ( indent != null && indent.length() > 0 ) stream.write( indent );
      stream.write( "</" );
      stream.write( tag );
      stream.write( '>' );
    }
    if ( indent != null ) stream.write( newline );
  }
}
